#include <purewell/purewell.hpp>
#include <purewell/condition.hpp>
#include <purewell/thread.hpp>
#include <purewell/mutex.hpp>
using namespace purewell;

#include <iostream>
#include <cstdarg>
using namespace std;

#include <sys/types.h>

class XCondSet
{
public:
	PWCondition& m_cond;
	PWMutex&	m_mutex;

public:
	XCondSet() : m_cond(*PWCondition::create(NULL)),
		m_mutex(*PWMutex::create(0))
	{
	}

	~XCondSet()
	{
		PWCondition::release(&m_cond);
		PWMutex::release(&m_mutex);
	}
};

class XLog
{
public:
	XLog() : m_mutex(*PWMutex::create(0))
	{
	}

	~XLog()
	{
		PWMutex::release(&m_mutex);
	}

	void format(const char* fmt, ...)
	{
		m_mutex.lock();
		va_list lst;
		char buf[1024*10];
		va_start(lst, fmt);
		vsprintf(buf, fmt, lst);
		va_end(lst);
		cerr << buf << endl;
		m_mutex.unlock();
	}

private:
	PWMutex&	m_mutex;
};

static XLog gLog;

class XThread : public PWThread
{
public:
	XThread() : m_number(++s_number)
	{
	}

public:
	void* onStart(void* value)
	{
		//detach();
		XCondSet& condset(*(XCondSet*)value);
		PWMutex& mutex(condset.m_mutex);
		PWCondition& cond(condset.m_cond);

		while ( true )
		{
			gLog.format("XThread(%u) is waitting...", m_number);
			condset.m_mutex.lock();
			cond.wait(mutex);
			condset.m_mutex.unlock();
			gLog.format("XThread(%u) is received signal...", m_number);
			break;
		}

		gLog.format("XThread(%u) is terminated...", m_number);
		return NULL;
	}

public:
	static volatile size_t s_number;
	size_t m_number;
};

class XThread2 : public XThread
{
public:
	void* onStart(void* value)
	{
		//detach();
		XCondSet& condset(*(XCondSet*)value);
		PWMutex& mutex(condset.m_mutex);
		PWCondition& cond(condset.m_cond);

		while ( true )
		{
			gLog.format("XThread2(%u) is waitting...", m_number);
			condset.m_mutex.lock();
			cond.wait(mutex);
			condset.m_mutex.unlock();
			gLog.format("XThread2(%u) is received signal...", m_number);
			break;
		}

		gLog.format("XThread2(%u) is terminated...", m_number);
		return NULL;
	}
};

volatile size_t XThread::s_number(0);

int
main(int,char**)
{
	const size_t noth(10);
	XThread thr[noth];
	XCondSet condset;

	for ( size_t i(0); i < noth; i++ )
	{
		thr[i].initialize(&condset);
		usleep(1);
	}

	usleep(5);

	for ( size_t i(0); i < noth; i++ )
	{
		condset.m_mutex.lock();
		condset.m_cond.signal();
		gLog.format("signal from main thread...");
		condset.m_mutex.unlock();
	}

	usleep(5);

	for ( size_t i(0); i < noth; i++ )
	{
		thr[i].wait(NULL);
		thr[i].destroy();
	}

	XThread2 thr2[noth];
	for ( size_t i(0); i < noth; i++ )
	{
		thr2[i].initialize(&condset);
		usleep(1);
	}

	usleep(5);
	condset.m_mutex.lock();
	condset.m_cond.broadcast();
	condset.m_mutex.unlock();
	gLog.format("broadcast from main thread...");

	usleep(5);

	for ( size_t i(0); i < noth; i++ )
	{
		thr2[i].wait(NULL);
		thr2[i].destroy();
	}
}

